<?php

namespace app\sys\service;

use think\admin\Exception;
use think\admin\Service;
use think\admin\service\AdminService;
use think\admin\storage\LocalStorage;

class fileService extends Service
{
    /**
     * 上传文件检查
     * @param $saveFileName
     * @param $extension
     * @return array
     * @throws Exception
     */
    public static function performFileSecurityCheck($saveFileName, $extension): array
    {
        [$uuid, $unid, $unexts] = self::initUnid();
        // 检查文件名称是否合法
        if (strpos($saveFileName, '../') !== false) {
            return [false, '文件路径不能出现跳级操作！'];
        }
        // 检查文件后缀是否被恶意修改
        if (strtolower(pathinfo(parse_url($saveFileName, PHP_URL_PATH), PATHINFO_EXTENSION)) !== $extension) {
            return [false, '文件后缀异常，请重新上传文件！'];
        }
        // 屏蔽禁止上传指定后缀的文件
        if (!in_array($extension, str2arr(self::getExt(self::getType())))) {
            return [false, '文件类型受限，请在后台配置规则！'];
        }
        // 前端用户上传后缀检查处理
        if (empty($uuid) && $unid > 0 && !in_array($extension, $unexts)) {
            return [false, '文件类型受限，请上传允许的文件类型！'];
        }
        if (in_array($extension, ['sh', 'asp', 'bat', 'cmd', 'exe', 'php'])) {
            return [false, '文件安全保护，禁止上传可执行文件！'];
        }
        return [true, '校验成功'];
    }

    /**
     * 移动文件
     * @param $file
     * @param $saveFileName
     * @return array
     */
    public static function moveUploadedFile($file, $saveFileName): array
    {
        $safeMode = self::getSafe();
        $local = LocalStorage::instance();
        $distName = $local->path($saveFileName, true);
        if (PHP_SAPI === 'cli') {
            is_dir(dirname($distName)) || mkdir(dirname($distName), 0777, true);
            rename($file->getPathname(), $distName);
        } else {
            $file->move(dirname($distName), basename($distName));
        }
        return $local->info($saveFileName, true, $file->getOriginalName());
    }

    /**
     * 检查图片是否安全
     * @param string $filename
     * @return bool
     */
    public static function imgNotSafe(string $filename): bool
    {
        $source = fopen($filename, 'rb');
        if (($size = filesize($filename)) > 512) {
            $hexs = bin2hex(fread($source, 512));
            fseek($source, $size - 512);
            $hexs .= bin2hex(fread($source, 512));
        } else {
            $hexs = bin2hex(fread($source, $size));
        }
        if (is_resource($source)) fclose($source);
        $bins = hex2bin($hexs);
        /* 匹配十六进制中的 <% ( ) %> 或 <? ( ) ?> 或 <script | /script> */
        foreach (['<?php ', '<% ', '<script '] as $key) if (stripos($bins, $key) !== false) return true;
        return boolval(preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexs));
    }

    /**
     * 初始化用户状态
     * @param boolean $check
     * @return array
     * @throws Exception
     */
    public static function initUnid(bool $check = true): array
    {
        $uuid = AdminService::getUserId();
        [$unid, $exts] = AdminService::withUploadUnid();
        if ($check && empty($uuid) && empty($unid)) {
            throw new Exception('未登录，禁止使用文件上传！');
        } else {
            return [$uuid, $unid, $exts];
        }
    }
    /**
     * 获取上传方式
     * @return string
     * @throws Exception
     */
    public static function getType(): string
    {
        $type = strtolower(input('type', ''));
        if (in_array($type, ['local', 'qiniu', 'alioss', 'txcos'])) { //, 'uptype'
            return $type;
        } else {
            return strtolower(sysconfig('SYS_BASE', 'SQM_SYS_DEFAULT_FILE_ENGINE'));
        }
    }

    /**
     * 获取文件上传类型
     * @return bool
     */
    public static function getSafe(): bool
    {
        return boolval(input('safe', '0'));
    }

    /**
     * 获取允许的扩展名称
     * @param string $type
     * @return string
     * @throws Exception
     */
    public static function getExt(string $type): string
    {
        switch ($type) {
            case "qiniu":
                $category = 'FILE_QINIU';
                $configKey = 'SQM_FILE_QINIU_ALLOW_EXTS';
                break;
            case "alioss":
                $category = 'FILE_ALIYUN';
                $configKey = 'SQM_FILE_ALIYUN_ALLOW_EXTS';
                break;
            case "txcos":
                $category = 'FILE_TENCENT';
                $configKey = 'SQM_FILE_TENCENT_ALLOW_EXTS';
                break;
            default:
                $category = 'FILE_LOCAL';
                $configKey = 'SQM_FILE_LOCAL_ALLOW_EXTS';
                break;
        }
        return strtolower(sysconfig($category, $configKey));
    }

    /**
     * 获取令牌桶名称
     * @param string $type
     * @return string
     * @throws Exception
     */
    public static function getBucket(string $type): string
    {
        if(!in_array($type,['qiniu','alioss','txcos','local'])) return 'publicUpload';
        switch ($type) {
            case "qiniu":
                $category = 'FILE_QINIU';
                $configKey = 'SQM_FILE_QINIU_BUCKET_NAME';
                break;
            case "alioss":
                $category = 'FILE_ALIYUN';
                $configKey = 'SQM_FILE_ALIYUN_BUCKET_NAME';
                break;
            case "txcos":
                $category = 'FILE_TENCENT';
                $configKey = 'SQM_FILE_TENCENT_BUCKET_NAME';
                break;
            default:
                break;
        }
        return $type !== 'local' ? sysconfig($category, $configKey) : 'publicUpload';
    }
}